From f0cb8942c47735c4fb0ab1e2dc8c5e6771302711 Mon Sep 17 00:00:00 2001 From: Steven Hand Date: Fri, 22 Sep 2006 12:14:22 +0100 Subject: [PATCH] Fix vmxassist to boot Vista. Currently, guest_linear_to_real to_real() in vmxassist assumes guest HVM use 2 level page table when entering protect mode with PG enabled. Vista uses 3 level (PAE enabled) page table, so memory addressing is wrong. This patch fixes it, by considering all 4 possible cases. Signed-off-by: Xiaowei Yang --- tools/firmware/vmxassist/machine.h | 1 + tools/firmware/vmxassist/vm86.c | 79 ++++++++++++++++++++++++------ 2 files changed, 64 insertions(+), 16 deletions(-) diff --git a/tools/firmware/vmxassist/machine.h b/tools/firmware/vmxassist/machine.h index 82fa12965d..0ea2adfa84 100644 --- a/tools/firmware/vmxassist/machine.h +++ b/tools/firmware/vmxassist/machine.h @@ -36,6 +36,7 @@ #define CR4_VME (1 << 0) #define CR4_PVI (1 << 1) #define CR4_PSE (1 << 4) +#define CR4_PAE (1 << 5) #define EFLAGS_ZF (1 << 6) #define EFLAGS_TF (1 << 8) diff --git a/tools/firmware/vmxassist/vm86.c b/tools/firmware/vmxassist/vm86.c index 52a8ae236d..1918785284 100644 --- a/tools/firmware/vmxassist/vm86.c +++ b/tools/firmware/vmxassist/vm86.c @@ -52,29 +52,74 @@ char *states[] = { static char *rnames[] = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di" }; #endif /* DEBUG */ +#define PDE_PS (1 << 7) #define PT_ENTRY_PRESENT 0x1 +/* We only support access to <=4G physical memory due to 1:1 mapping */ static unsigned -guest_linear_to_real(unsigned long base, unsigned off) +guest_linear_to_real(uint32_t base) { - unsigned int gcr3 = oldctx.cr3; - unsigned int l1_mfn; - unsigned int l0_mfn; + uint32_t gcr3 = oldctx.cr3; + uint64_t l2_mfn; + uint64_t l1_mfn; + uint64_t l0_mfn; if (!(oldctx.cr0 & CR0_PG)) - return base + off; + return base; - l1_mfn = ((unsigned int *)gcr3)[(base >> 22) & 0x3ff ]; - if (!(l1_mfn & PT_ENTRY_PRESENT)) - panic("l2 entry not present\n"); - l1_mfn = l1_mfn & 0xfffff000 ; + if (!(oldctx.cr4 & CR4_PAE)) { + l1_mfn = ((uint32_t *)gcr3)[(base >> 22) & 0x3ff]; - l0_mfn = ((unsigned int *)l1_mfn)[(base >> 12) & 0x3ff]; - if (!(l0_mfn & PT_ENTRY_PRESENT)) - panic("l1 entry not present\n"); - l0_mfn = l0_mfn & 0xfffff000; + if (oldctx.cr4 & CR4_PSE || l1_mfn & PDE_PS) { + /* 1 level page table */ + l0_mfn = l1_mfn; + if (!(l0_mfn & PT_ENTRY_PRESENT)) + panic("l1 entry not present\n"); - return l0_mfn + off + (base & 0xfff); + l0_mfn &= 0xffc00000; + return l0_mfn + (base & 0x3fffff); + } + + if (!(l1_mfn & PT_ENTRY_PRESENT)) + panic("l2 entry not present\n"); + + l1_mfn &= 0xfffff000; + l0_mfn = ((uint32_t *)l1_mfn)[(base >> 12) & 0x3ff]; + if (!(l0_mfn & PT_ENTRY_PRESENT)) + panic("l1 entry not present\n"); + l0_mfn &= 0xfffff000; + + return l0_mfn + (base & 0xfff); + } else if (oldctx.cr4 & CR4_PAE && !(oldctx.cr4 & CR4_PSE)) { + l2_mfn = ((uint64_t *)gcr3)[(base >> 30) & 0x3]; + if (!(l2_mfn & PT_ENTRY_PRESENT)) + panic("l3 entry not present\n"); + l2_mfn &= 0x3fffff000ULL; + + l1_mfn = ((uint64_t *)l2_mfn)[(base >> 21) & 0x1ff]; + if (!(l1_mfn & PT_ENTRY_PRESENT)) + panic("l2 entry not present\n"); + l1_mfn &= 0x3fffff000ULL; + + l0_mfn = ((uint64_t *)l1_mfn)[(base >> 12) & 0x1ff]; + if (!(l0_mfn & PT_ENTRY_PRESENT)) + panic("l1 entry not present\n"); + l0_mfn &= 0x3fffff000ULL; + + return l0_mfn + (base & 0xfff); + } else { /* oldctx.cr4 & CR4_PAE && oldctx.cr4 & CR4_PSE */ + l1_mfn = ((uint64_t *)gcr3)[(base >> 30) & 0x3]; + if (!(l1_mfn & PT_ENTRY_PRESENT)) + panic("l2 entry not present\n"); + l1_mfn &= 0x3fffff000ULL; + + l0_mfn = ((uint64_t *)l1_mfn)[(base >> 21) & 0x1ff]; + if (!(l0_mfn & PT_ENTRY_PRESENT)) + panic("l1 entry not present\n"); + l0_mfn &= 0x3ffe00000ULL; + + return l0_mfn + (base & 0x1fffff); + } } static unsigned @@ -95,7 +140,8 @@ address(struct regs *regs, unsigned seg, unsigned off) (mode == VM86_REAL_TO_PROTECTED && regs->cs == seg)) return ((seg & 0xFFFF) << 4) + off; - entry = ((unsigned long long *) guest_linear_to_real(oldctx.gdtr_base, 0))[seg >> 3]; + entry = ((unsigned long long *) + guest_linear_to_real(oldctx.gdtr_base))[seg >> 3]; entry_high = entry >> 32; entry_low = entry & 0xFFFFFFFF; @@ -780,7 +826,8 @@ load_seg(unsigned long sel, uint32_t *base, uint32_t *limit, union vmcs_arbytes return 1; } - entry = ((unsigned long long *) guest_linear_to_real(oldctx.gdtr_base, 0))[sel >> 3]; + entry = ((unsigned long long *) + guest_linear_to_real(oldctx.gdtr_base))[sel >> 3]; /* Check the P bit first */ if (!((entry >> (15+32)) & 0x1) && sel != 0) -- 2.30.2